package com.simple.trade.service.india.impl;

import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import com.simple.common.entity.ReqContext;
import com.simple.common.entity.ResData;
import com.simple.common.entity.constant.Payment;
import com.simple.common.entity.dto.pay.india.IndiaBaseDto;
import com.simple.common.entity.dto.pay.india.IndiaRspDto;
import com.simple.common.entity.po.sys.*;
import com.simple.common.entity.req.india.req.WithdrawalBase;
import com.simple.common.entity.req.india.res.IndiaBaseRes;
import com.simple.trade.holder.PayContext;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.apache.commons.lang3.tuple.Pair;

import java.lang.reflect.ParameterizedType;
import java.sql.Time;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.*;
import java.util.function.Consumer;

import static com.simple.common.entity.req.india.req.IndiaWithdrawalReq.BANK;
import static com.simple.common.entity.req.india.req.IndiaWithdrawalReq.WALLET;

/**
 * 支付相关
 *
 * @author yasud
 * @date 2020年09月30日 09:26
 * @description
 */
@Slf4j
public abstract class IndiaWithdrawalBaseService<R extends IndiaBaseRes, T extends IndiaRspDto> extends IndiaAbstractService {

    /**
     * 上下文
     */
    private final ThreadLocal<Map<String, Object>> CONTEXT = new InheritableThreadLocal<>();

    /**
     * 下单逻辑
     *
     * @param req
     * @return
     */
    public ResData<R> call(WithdrawalBase req,
                           Consumer<Trade> consumer) {
        try {
            log.info("商户订单【{}】印度代付申请下单{},时间为：{}", req.getOrderNo(), "开始", LocalDateTime.now());
            HashMap<String, Object> map = Maps.newHashMap();
            map.put("merchant_order_no", req.getOrderNo());
            map.put("app_id", PayContext.current().exp().getAppId());
            List<Trade> tradeList = tradeApi.findByProperty_(map);
            log.info("商户订单【{}】印度代付申请下单{},结果为：{}", req.getOrderNo(), "重复订单检查", CollectionUtils.isNotEmpty(tradeList) ? "重复订单" : "新订单");
            Preconditions.checkState(CollectionUtils.isEmpty(tradeList), "duplicate order");
            Pair<ChannelComp, Product> pair = findChannelAnProduct();
            Application application = PayContext.current().app();
            ChannelComp channel = pair.getLeft();
            Product product = pair.getRight();
            Trade trade = generateTrade(req, channel, product, application, consumer);
            trade = tradeApi.save_(trade);
            log.info("商户订单【{}】印度代付申请下单{},订单为：{}", req.getOrderNo(), "订单入库", gson.toJson(trade));
            ResData<T> resData = forwardToChannel(getDto(trade, PayContext.current().getContext(), channel, product), channel, Payment.PAY_TYPE.INDIA_WITHDRAWAL, getTClass());
            Long tradeId = trade.getId();
            log.info("平台订单号[{}]印度代付执行{}结果为：{}", tradeId, "渠道执行结束", gson.toJson(resData));
            if (resData.isOk()) {
                T rsp = resData.getData();
                Trade trade1 = rsp.getTrade();
                Preconditions.checkNotNull(trade1, "Order is empty, please contact customer service!!!!");
                tradeApi.updateById_(trade1);
                context(ImmutableMap.of(
                        "channel", channel,
                        "product", product
                ));
                ResData ok = ResData.ok(map(rsp));
                log.info("平台订单号[{}]印度代付执行{}响应saas结果为：{}", tradeId, "成功结束", gson.toJson(ok));
                return ok;
            } else {
                String msg = resData.getMsg();
                trade.setStatus(Trade.Status.FAILED);
                trade.setMsg(msg);
                trade.setChannelStatus("未知");
                trade.setChannelMsg(msg);
                tradeApi.updateById_(trade);
                ResData error = ResData.error(msg);
                log.info("平台订单号[{}]印度代付执行{}响应saas结果为：{}", tradeId, "失败结束", gson.toJson(error));
                return error;
            }
        } finally {
            CONTEXT.remove();
        }
    }

    /**
     * 上下文设置
     *
     * @param map
     */
    private void context(Map<String, Object> map) {
        CONTEXT.set(map);
    }

    /**
     * 上下文获取这的map 没法设置
     *
     * @return
     */
    protected Map<String, Object> context() {
        return CONTEXT.get();
    }

    /**
     * 获取实际类型
     *
     * @return
     */
    private Class<T> getTClass() {
        Class<T> tClass = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[1];
        return tClass;
    }

    /**
     * 得到dto
     *
     * @return
     */
    protected abstract IndiaBaseDto getDto(Trade trade, ReqContext reqContext, ChannelComp channel, Product product);

    /**
     * 结果转化
     *
     * @param rsp
     * @return
     */
    protected abstract R map(T rsp);

    /**
     * 订单入库
     *
     * @param req
     * @param channel
     * @param product
     * @return
     */
    @SneakyThrows
    protected Trade generateTrade(WithdrawalBase req,
                                  ChannelComp channel,
                                  Product product,
                                  Application application,
                                  Consumer<Trade> consumer) {
        Trade trade = new Trade();
        Merchant merchant = PayContext.current().merchant();
        Application app = PayContext.current().app();
        AppExample exp = PayContext.current().exp();
        //设置订单所属
        trade.setMerchantNo(merchant.getMerchantNo());
        trade.setMerchantName(merchant.getName());
        trade.setApplicationId(app.getId());
        trade.setApplicationName(application.getName());
        trade.setApplicationType(app.getType());
        trade.setAppId(exp.getAppId());
        trade.setAppName(exp.getName());

        trade.setMerchantOrderNo(req.getOrderNo());
        trade.setAmount(req.getTransferAmount());
        trade.setCurrency("INR");
        trade.setConvertOrderAmount(req.getTransferAmount());
        trade.setConvertOrderCurrency("INR");
        //默认情况下这里支付的金额以及币种和商户传给我的是一样的
        trade.setPayAmount(req.getTransferAmount());
        trade.setPayAmountCurrency("INR");
        trade.setProductId(product.getId());
        trade.setProductName(product.getName());

        trade.setStatus(Trade.Status.PENDING);
        trade.setMsg(Trade.Status.PENDING.name());

        trade.setChannelCode(channel.getCode());
        trade.setChannelId(channel.getId());
        trade.setChannelName(channel.getName());
        trade.setOrderDesc(req.getAsStringFromExtra("remark"));

        ChannelComp channelComp = PayContext.current().channel();
        ChannelMerchant channelMerchant = channelComp.getChannelMerchant();
        trade.setChannelMerchantId(channelComp.getChannelMerchantId());
        trade.setChannelMerchantName(channelMerchant.getName());

        ExampleChannel expChannel = PayContext.current().expChannel();
        trade.setExpChannelId(expChannel.getId());

        trade.setYmd(DateFormatUtils.format(new Date(), "yyyy-MM-dd"));
        trade.setTime(Time.valueOf(LocalTime.now()));
        //直连没有同步跳转地址
        //直连简单的没有shipping, billing, product  的信息
        trade.setExtraInfo(objectMapper.writeValueAsString(req.getExtraInfo()));
        trade.setRemark1(gson.toJson(withdrawalInfo(req)));
        trade.setType(Trade.Type.NORMAL_TRADE_WITHDRAWAL);

        trade.setAccName(req.getPayeeName());
        trade.setAccNumber(req.getPayeeAccount());
        trade.setWithdrawalType(transferWithdrawalType(req.getTransferType()));
        trade.setAuditStatus(Trade.AuditStatus.AUDIT_PROCESS);
        trade.setOriginator("商户发起");
        consumer.accept(trade);
        return trade;
    }

    private Trade.WithdrawalType transferWithdrawalType(String payType) {
        if (StringUtils.isBlank(payType)) {
            return Trade.WithdrawalType.NONE;
        }
        switch (payType) {
            case BANK:
                return Trade.WithdrawalType.IMPS;
            case WALLET:
                return Trade.WithdrawalType.UPI;
            default:
                return Trade.WithdrawalType.NONE;
        }
    }

    /**
     * 代付信息
     *
     * @param req
     * @return
     */
    private Map<String, Object> withdrawalInfo(WithdrawalBase req) {
        Map<String, Object> map = Maps.newHashMap();
        map.put("payAccount", req.getPayeeAccount());
        map.put("payType", req.getTransferType());
        String accountOwner = req.getPayeeName();
        if (StringUtils.isNotBlank(accountOwner)) {
            map.put("accountOwner", accountOwner);
        }
        String bankName = req.getBankName();
        if (StringUtils.isNotBlank(bankName)) {
            map.put("bankName", bankName);
        }

        String bankCode = req.getBankCode();
        if (StringUtils.isNotBlank(bankCode)) {
            map.put("bankCode", bankCode);
        }
        return map;
    }
}
